﻿using System;
using System.Collections.Generic;
using System.IO;

namespace ExtendedStream
{
    /// <summary>
    /// This class act like a queue of buffers each write add buffer to end of queue and each read get buffer from start of queue
    /// </summary>
    class QueueStream:Stream
    {
        #region Fields
        private readonly LinkedList<byte[]> _buffers;
        private long _length;
        private int _inBufferPosition;
        #endregion

        #region Constructors

        public QueueStream()
        {
            _buffers = new LinkedList<byte[]>();
            _length = 0;
            _inBufferPosition = 0;
        }
        #endregion

        #region Overrides of Stream

        public override void Flush()
        {
            lock (this)
            {
                _buffers.Clear();
                _length = 0; 
            }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            lock (this)
            {
                if (offset < 0)
                    throw new Exception("Negetive Offset!");
                if (count < 0)
                    throw new Exception("Negetive Count!");

                if (count > _length)
                    count = (int)_length;

                int result = 0;
                while (count > 0)
                {
                    if (_buffers.Count == 0)
                        return result;
                    int readSize = Math.Min(_buffers.First.Value.Length - _inBufferPosition, count);
                    Array.Copy(_buffers.First.Value, _inBufferPosition, buffer, offset, readSize);
                    offset += readSize;
                    count -= readSize;
                    result += readSize;
                    _inBufferPosition += readSize;
                    _length -= _inBufferPosition;
                    if (_inBufferPosition == _buffers.First.Value.Length)
                    {
                        _buffers.RemoveFirst();
                    }
                }
                return result;
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            lock (this)
            {
                if (offset < 0)
                    throw new Exception("Negetive Offset!");
                if (count < 0)
                    throw new Exception("Negetive Count!");
                if (count == 0)
                    return;

                byte[] secondoryBuffer = new byte[count];
                Array.Copy(buffer, offset, secondoryBuffer, 0, count);
                _buffers.AddLast(secondoryBuffer);
                _length += count; 
            }
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Length
        {
            get
            {
                lock (this)
                {
                    return _length;
                }
            }
        }

        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        #endregion
    }
}
